#include "connect.h"

extern argument arg;

static int connect_nonb(int sockfd, struct sockaddr *saptr, socklen_t salen, int nsec);

static int recv_nonb(int sockfd, void *buf, int len, unsigned int rflags, int nsec);

/**
 * connect try
 * 
 * -1 init failed
 * -2 connect failed
 *  1 success
 */
int connect_probe(site_block *b)
{
	int fd, maxfd, r = 1, s, error, len;
	long flags;
	fd_set wset;
	struct timeval timeout;
	struct addrinfo hints, *addr;
	char buf[1024] = {'\0'};
	char *p;
	
	//site = gethostbyname_my(host);

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family=AF_INET;  
	hints.ai_socktype=SOCK_STREAM;  
	hints.ai_flags = AI_PASSIVE;
	
	// set default value 
	strcpy(b->http_status, "0");
	b->state = 0;

	if((s = getaddrinfo(b->server, b->port, &hints, &addr)) != 0) {
		return 0;
	}
	if ((fd = socket(addr->ai_family,addr->ai_socktype,addr->ai_protocol)) < 0) {
		return 0;
	}
	if (connect_nonb(fd, addr->ai_addr,addr->ai_addrlen, CONNECT_TIMEOUT) != 0) {
		return 0;
	}
	sprintf(buf, arg.http_header, "/", b->server); 

	// send
	len = strlen(buf) - 1;
	if ((s = send(fd, buf, len, 0)) == -1) return 0;
	assert(len == s);
	b->state = 1;
	// recv
	memset(buf, '\0', 20);
	if (recv_nonb(fd, buf, 20, 0, RECV_TIMEOUT) == -1) return 0;
	s = 0;
	while (s < 3) {
		*(b->http_status + s) = *(buf + 9 + s);
		s++;
	}
	*(b->http_status + s) = '\0';
	close(fd);
	freeaddrinfo(addr);
	return r;
}

/**
 *
 */
static int connect_nonb(int sockfd, struct sockaddr *saptr, socklen_t salen, int nsec) 
{ 
     int     flags, n, error; 
     socklen_t len; 
     fd_set rset, wset; 
     struct timeval tval; 
 
     flags = fcntl(sockfd, F_GETFL, 0); 
     fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 
 
     error = 0; 
     if ( (n = connect(sockfd, saptr, salen)) < 0) 
         if (errno != EINPROGRESS)  {
			error = 1;
			goto done;
		 }
             //return (-1); 
 
     /* Do whatever we want while the connect is taking place. */ 
 
     if (n == 0) 
         goto done;               /* connect completed immediately */ 
 
     FD_ZERO(&rset); 
     FD_SET(sockfd, &rset); 
     wset = rset; 
     tval.tv_sec = nsec; 
     tval.tv_usec = 0; 
 
     if ( (n = select(sockfd + 1, &rset, &wset, NULL, 
                     nsec ? &tval : NULL)) == 0) { 
         /* timeout */ 
		 error = 1;
		 goto done;
     } 
 
     if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { 
         len = sizeof(error); 
         if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
              /* Solaris pending error */ 
			 error = 1;
			 goto done;
		 }
     } else {
		error = 1;
		goto done;
	 }	
 
   done: 
     fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */ 
 
     if (error) { 
         close(sockfd);           /* just in case */ 
         errno = error; 
         return (-1); 
     } 
     return (0); 
} 

static int recv_nonb(int sockfd, void *buf, int len, unsigned int rflags, int nsec)
{ 
     int     flags, n; 
     fd_set  rset; 
     struct timeval tval; 
	 int i = 0;
 
     flags = fcntl(sockfd, F_GETFL, 0); 
     fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 
 
	 for (;;) {
		 FD_ZERO(&rset); 
		 FD_SET(sockfd, &rset); 
		 tval.tv_sec = nsec; 
		 tval.tv_usec = 0; 
	 
		 if ( (n = select(sockfd + 1, &rset, NULL, NULL, 
						 nsec ? &tval : NULL)) == 0) { 
			 /* timeout */ 
			 goto error_block;
		 } 
	 
		 if (FD_ISSET(sockfd, &rset)) {
			 if ((n = recv(sockfd, buf + i, len - i, rflags)) < 0) {
				if (errno != EWOULDBLOCK) goto error_block;
			 } else if (n == 0) goto done;
			 else {
				i = n;
		     }	
		 } 
	 }

done:
     fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */ 
     return (0);

error_block:
	fcntl(sockfd, F_SETFL, flags);
	close(sockfd);
	return (-1);
}
